/*
 * OpenParts
 * A dynamic-loading components framework for GWT
 * Copyright (C) 2011 Christophe Bouthier  [chris{AT}binary-gastronome{DOT}fr]
 *
 * This work is partially based on work I have done at INRIA (http://www.inria.fr) 
 * in the context of the Qualipso European Project (http://qualipso.org/),
 * The work done at INRIA is Copyright (C) 2006-2011 INRIA
 *
 * This work is distributed under the LGPL version 3
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License version 3
 * as published by the Free Software Foundation. See the GNU
 * Lesser General Public License in LGPL.txt for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */
package fr.openparts.OpenParts.rebind;


import com.google.gwt.core.ext.TreeLogger;
import com.google.gwt.core.ext.UnableToCompleteException;
import com.google.gwt.core.ext.typeinfo.JClassType;
import com.google.gwt.user.rebind.SourceWriter;

import fr.openparts.OpenParts.client.annotations.OPEventHandler;
import fr.openparts.OpenParts.client.annotations.OPViewFactory;

/**
 * Write the necessary code in the generated binder implementation.
 * 
 * @author Christophe Bouthier [chris{AT}binary-gastronome{DOT}fr]
 * @creation.date 26 December 2009
 */
public class OPBinderWriter {

    /**
     * Name of the generated var containing the service name.
     */
    protected static final String VAR_SERVICE_NAME = "serviceName";

    /**
     * Name of the generated var containing the resources names.
     */
    protected static final String VAR_RESOURCES_NAMES = "resourcesNames";

    /**
     * Name of the generated method binding the listeners to the event bus.
     */
    protected static final String METHOD_BIND_LISTENERS = "bindListeners";

    /**
     * Name of the generated method binding the view display factories.
     */
    protected static final String METHOD_BIND_VIEWS = "bindViews";

    /**
     * Name of the generated method registering the binder as a event handler callback.
     */
    protected static final String METHOD_REGISTER_CALLBACK = "registerMeAsCallback";

    /**
     * Name of the generated method registered as event handler callback.
     */
    protected static final String METHOD_EVENT_HANDLER_CALLBACK = "eventHandlerCallback";

    /**
     * Prefix of the name of the generated method building the data tree for a view.
     */
    protected static final String METHOD_PREFIX_BUILD_DATA_TREE = "buildDataTree";

    /**
     * Prefix of the name of the generated method registering the binder as a view factory for a view.
     */
    protected static final String METHOD_PREFIX_REGISTER_VIEW_FACTORY = "registerMeAsViewFactory";

    /**
     * Prefix of the name of the generated method registered as a view factory for a view.
     */
    protected static final String METHOD_PREFIX_VIEW_FACTORY_CALLBACK = "viewFactory";

    /**
     * Type of the OPParamsJS, as it is a parameters of a JSNI view factory callback.
     */
    protected static final String TYPE_OPPARAMSJS = "fr/openparts/OpenParts/client/OPParamsJS";

    /**
     * The source writer to the generated binder.
     */
    protected final SourceWriter sourceWriter;

    /**
     * The part that will be binded by the generated binder. Contains all necessary annotations.
     */
    protected final JClassType part;

    /**
     * The simple name of the generated binder.
     */
    protected final String generatedBinderName;

    /**
     * The fully qualified name of the generated binder.
     */
    protected final String generatedBinderFullName;

    /**
     * The logger, passed by the GWT framework.
     */
    protected final TreeLogger logger;

    /**
     * Data structure containing all informations necessary to build the binder. Filled up from the part annotated data.
     */
    protected final OPBinderBuildingData buildingData;

    /**
     * Constructor. Parse the part to get all necessary annotations and build the building data structure.
     * 
     * @param sourceWriter
     *            the source writer to the generated binder. Shouldn't be null.
     * @param part
     *            the part that will be binded by the generated binder. Contains all necessary annotations. Shouldn't be null.
     * @param generatedBinderName
     *            the simple name of the generated binder. Shouldn't be null.
     * @param generatedBinderFullName
     *            the fully qualified name of the generated binder. Shouldn't be null.
     * @param logger
     *            the logger, passed by the GWT framework. Shouldn't be null.
     * @throws UnableToCompleteException
     *             if something goes wrong.
     */
    public OPBinderWriter(final SourceWriter sourceWriter, final JClassType part, final String generatedBinderName, final String generatedBinderFullName,
            final TreeLogger logger) throws UnableToCompleteException {
        assert sourceWriter != null : "parameter sourceWriter shouldn't be null";
        assert part != null : "parameter part shouldn't be null";
        assert generatedBinderName != null : "parameter generatedBinderName shouldn't be null";
        assert generatedBinderFullName != null : "parameter generatedBinderFullName shouldn't be null";
        assert logger != null : "parameter logger shouldn't be null";

        this.sourceWriter = sourceWriter;
        this.part = part;
        this.generatedBinderName = generatedBinderName;
        this.generatedBinderFullName = generatedBinderFullName;
        this.logger = logger;

        buildingData = new OPBinderBuildingData(part, logger);
    }

    /**
     * Write the necessary code in the generated binder. Several steps are necessary:
     * <ul>
     * <li>generate the attributes</li>
     * <li>generate the callback for completed loading</li>
     * <li>generate the bindPart(component) method</li>
     * <li>generate the code for the listeners</li>
     * <li>generate the code for the resources/views</li>
     * <li>generate the service helpers methods</li>
     * <li>generate the resources helpers methods</li>
     * </ul>
     */
    public void writeBinder() {
        sourceWriter.println();
        writeAttributes();
        sourceWriter.println();
        writeLoadingCompleted();
        sourceWriter.println();
        writeBindPart();
        sourceWriter.println();
        writeListeners();
        sourceWriter.println();
        writeViews();
        sourceWriter.println();
        writeServiceHelper();
        sourceWriter.println();
        writeResourcesHelper();
        sourceWriter.println();

        sourceWriter.commit(logger);
    }

    /**
     * Write the binder's attributes.
     */
    protected void writeAttributes() {
        sourceWriter.indent();
        sourceWriter.println("protected " + part.getSimpleSourceName() + " part;");
        sourceWriter.println("protected final String " + VAR_SERVICE_NAME + " = \"" + buildingData.getServiceName() + "\";");
        sourceWriter.print("protected final String[] " + VAR_RESOURCES_NAMES + "= {");
        for (int i = 0; i < buildingData.getAllRegisteredResources().length; i++) {
            String resourceName = buildingData.getAllRegisteredResources()[i];
            if (i != 0) {
                sourceWriter.print(", ");
            }
            sourceWriter.print("\"" + resourceName + "\"");
        }
        sourceWriter.println("};");
        sourceWriter.outdent();
    }

    /**
     * Write the public method that will be called once the part has finished loading and binding itself.
     */
    protected void writeLoadingCompleted() {
        sourceWriter.indent();
        sourceWriter.println("public void notifyLoadingCompleted() {");
        sourceWriter.indent();
        sourceWriter.println("final String serviceName = getServiceName();");
        sourceWriter.println("assert serviceName != null : \"service name shouldn't be null\";");
        sourceWriter.println("OPShell.notifyLoadingCompleted(serviceName);");
        sourceWriter.outdent();
        sourceWriter.println("}");
        sourceWriter.outdent();
    }

    /**
     * Write the public method that will be called to do all the necessary binding. It will call the necessary methods to register the listeners and the
     * resources actions.
     */
    protected void writeBindPart() {
        sourceWriter.indent();
        sourceWriter.println("public void bindPart(final " + part.getSimpleSourceName() + " part) {");
        sourceWriter.indent();
        sourceWriter.println("assert part != null : \"parameter part shouldn't be null\";");
        sourceWriter.println();
        sourceWriter.println("this.part = part;");
        sourceWriter.println(METHOD_BIND_LISTENERS + "();");
        sourceWriter.println(METHOD_BIND_VIEWS + "();");
        sourceWriter.outdent();
        sourceWriter.println("}");
        sourceWriter.outdent();
    }

    /**
     * Write the necessary method and sub methods in order to register all event bus listeners, that is all methods in the part that are annotated with
     * {@link OPEventHandler} annotation.
     */
    protected void writeListeners() {
        writeListenersBindListeners();
        sourceWriter.println();
        writeListenersRegisterMeAsCallback();
        sourceWriter.println();
        writeListenersEventHandlerCallback();
    }

    /**
     * Write the main method for binding listeners.
     */
    protected void writeListenersBindListeners() {
        sourceWriter.indent();
        sourceWriter.println("protected void " + METHOD_BIND_LISTENERS + "() {");
        sourceWriter.indent();
        sourceWriter.println(METHOD_REGISTER_CALLBACK + "(OPShell.opBindingSharedInstance(), this);");
        sourceWriter.outdent();
        sourceWriter.println("}");
        sourceWriter.outdent();
    }

    /**
     * Write the binder method registering itself as callback.
     */
    protected void writeListenersRegisterMeAsCallback() {
        sourceWriter.indent();
        sourceWriter.println("protected final native void " + METHOD_REGISTER_CALLBACK + "(final OPBinding binding, final " + generatedBinderName
                + " binder) /*-{");
        sourceWriter.indent();
        sourceWriter.println("binding.listeners[\"" + buildingData.serviceName + "\"] = function(event, paramsjs) {");
        sourceWriter.indent();
        sourceWriter.println("return $entry(binder.@" + generatedBinderFullName + "::" + METHOD_EVENT_HANDLER_CALLBACK + "(Ljava/lang/String;L" + TYPE_OPPARAMSJS
                + ";)(event, paramsjs));");
        sourceWriter.outdent();
        sourceWriter.println("}");
        sourceWriter.outdent();
        sourceWriter.println("}-*/;");
        sourceWriter.outdent();
    }

    /**
     * Write the event handler callback of the binder.
     */
    protected void writeListenersEventHandlerCallback() {
        sourceWriter.indent();
        sourceWriter.println("public void " + METHOD_EVENT_HANDLER_CALLBACK + "(final String event, final OPParamsJS paramsJS) {");
        sourceWriter.indent();
        sourceWriter.println("assert event != null : \"parameter event shouldn't be null\";");
        sourceWriter.println();
        sourceWriter.println("OPParams params = null;");
        sourceWriter.println("if (paramsJS != null) {");
        sourceWriter.indent();
        sourceWriter.println("params = paramsJS.toOPParams();");
        sourceWriter.outdent();
        sourceWriter.println("}");
        buildEventHandlerCallback();
        sourceWriter.outdent();
        sourceWriter.println("}");
        sourceWriter.outdent();
    }

    /**
     * Write the content of the binder's event handler callback.
     */
    protected void buildEventHandlerCallback() {
        assert buildingData != null : "builder data cannot be null";
        assert buildingData.getCallbacks() != null : "builder data callbacks cannot be null";

        // no callbacks to register
        if (buildingData.getCallbacks().isEmpty()) {
            return;
        }

        // only one all events callback to register, no filtering
        if (buildingData.getAllRegisteredEvents().length == 0) {
            assert buildingData.getCallbacks().size() == 1 : "there should be exactly one callback when no specific events are registered";

            sourceWriter.println("part." + buildingData.getCallbacks().get(0).getCallback().getName() + "(event, params);");
            return;
        }

        // some filtering to do
        OPBinderBuildingDataEventHandler allEventsCallback = null;
        for (OPBinderBuildingDataEventHandler callback : buildingData.getCallbacks()) {
            if (callback.getEvents().length == 0) {
                // all events callback need to be called last
                allEventsCallback = callback;
                continue;
            }

            buildFilterForEventCallback(callback);
        }
        if (allEventsCallback != null) {
            buildFilterForAllEventsCallback(allEventsCallback);
        }

    }

    /**
     * Write the code that do event filtering for the given part callback, in the binder's event handler callback.
     * 
     * @param callback
     *            the callback itself. Shouldn't be null.
     */
    protected void buildFilterForEventCallback(final OPBinderBuildingDataEventHandler callback) {
        assert callback != null : "parameter callback shouldn't be null";
        assert callback.getEvents().length > 0 : "parameter callback should contains specific events";

        if (callback.getEvents().length == 1) {
            sourceWriter.println("if (\"" + callback.getEvents()[0] + "\".equals(event)) {");
        } else {
            sourceWriter.print("if (");
            for (int i = 0; i < callback.getEvents().length; i++) {
                if (i != 0) {
                    sourceWriter.print(" || ");
                }
                sourceWriter.print("(\"" + callback.getEvents()[i] + "\".equals(event))");
            }
            sourceWriter.println(") {");
        }
        sourceWriter.indent();
        sourceWriter.println("part." + callback.getCallback().getName() + "(event, params);");
        sourceWriter.println("return;");
        sourceWriter.outdent();
        sourceWriter.println("}");
    }

    /**
     * Write the code that is called for all events that are not specific for the given part callback, in the binder's event handler callback.
     * 
     * @param allEventsCallback
     *            the callback itself. Shouldn't be null.
     */
    protected void buildFilterForAllEventsCallback(final OPBinderBuildingDataEventHandler allEventsCallback) {
        assert allEventsCallback != null : "parameter callback shouldn't be null";
        assert allEventsCallback.getEvents().length == 0 : "parameter callback should not contains specific events";

        sourceWriter.println("part." + allEventsCallback.getCallback().getName() + "(event, params);");
    }

    /**
     * Write the necessary method and sub methods in order to register all resources views, that is all methods in the part that are annotated with
     * {@link OPViewFactory} annotation.
     */
    protected void writeViews() {
        writeViewsBindViews();
        sourceWriter.println();
        writeViewsBuildDataTree();
        sourceWriter.println();
        writeViewsRegisterMeAsViewFactories();
        sourceWriter.println();
        writeViewsViewFactoryCallbacks();
    }

    /**
     * Write the main method for binding views.
     */
    protected void writeViewsBindViews() {
        sourceWriter.indent();
        sourceWriter.println("protected void " + METHOD_BIND_VIEWS + "() {");
        sourceWriter.indent();
        buildCallsToDataTreeMethods();
        sourceWriter.outdent();
        sourceWriter.println("}");
        sourceWriter.outdent();
    }

    /**
     * Returns the name of the method building the data tree for the given view.
     * 
     * @param factory
     *            all necessary methods for the view. Shouldn't be null.
     * @return the name of the method building the data tree for the given view.
     */
    protected String getBuildDataTreeMethodName(final OPBinderBuildingDataViewFactory factory) {
        assert factory != null : "parameter factory shouldn't be null";

        return METHOD_PREFIX_BUILD_DATA_TREE + factory.getServiceName() + factory.getResourceName() + factory.getActionName();
    }

    /**
     * Build the call to the methods building the data tree (service, resource, action) for all binded views.
     */
    protected void buildCallsToDataTreeMethods() {
        assert buildingData != null : "builder data cannot be null";
        assert buildingData.getFactories() != null : "builder data factories cannot be null";

        // no view factory to register
        if (buildingData.getFactories().isEmpty()) {
            return;
        }

        for (OPBinderBuildingDataViewFactory factory : buildingData.getFactories()) {
            sourceWriter.println(getBuildDataTreeMethodName(factory) + "();");
        }
    }

    /**
     * Write the method that will build, for each view, the corresponding data tree (service, resource, action).
     */
    protected void writeViewsBuildDataTree() {
        assert buildingData != null : "builder data cannot be null";
        assert buildingData.getFactories() != null : "builder data factories cannot be null";

        // no view factory to register
        if (buildingData.getFactories().isEmpty()) {
            return;
        }

        for (OPBinderBuildingDataViewFactory factory : buildingData.getFactories()) {
            buildDataTreeMethod(factory);
            sourceWriter.println();
        }
    }

    /**
     * Build the method that building the data tree (service, resource, action) for the given view.
     * 
     * @param factory
     *            all necessary methods for the view. Shouldn't be null.
     */
    protected void buildDataTreeMethod(final OPBinderBuildingDataViewFactory factory) {
        assert factory != null : "parameter factory shouldn't be null";

        sourceWriter.indent();
        sourceWriter.println("protected void " + getBuildDataTreeMethodName(factory) + "() {");
        sourceWriter.indent();
        sourceWriter.println("final OPBinding binding = OPShell.opBindingSharedInstance();");
        sourceWriter.println();
        sourceWriter.println("OPBindingService service = binding.getService(\"" + factory.getServiceName() + "\");");
        sourceWriter.println("if (service == null) {");
        sourceWriter.indent();
        sourceWriter.println("service = OPBindingService.serviceFactory(\"" + factory.getServiceName() + "\");");
        sourceWriter.println("binding.bind(service);");
        sourceWriter.outdent();
        sourceWriter.println("}");
        sourceWriter.println("assert service != null : \"service factory returned a null instance\";");
        sourceWriter.println();
        sourceWriter.println("OPBindingResource resource = binding.getResource(\"" + factory.getServiceName() + "\", \"" + factory.getResourceName() + "\");");
        sourceWriter.println("if (resource == null) {");
        sourceWriter.indent();
        sourceWriter.println("resource = OPBindingResource.resourceFactory(\"" + factory.getResourceName() + "\");");
        sourceWriter.println("service.bindResource(resource);");
        sourceWriter.outdent();
        sourceWriter.println("}");
        sourceWriter.println("assert resource != null : \"resource factory returned a null instance\";");
        sourceWriter.println();
        buildActionBinding(factory);
        sourceWriter.println("assert action != null : \"action factory returned a null instance\";");
        sourceWriter.println("resource.bindAction(action);");
        sourceWriter.println(getRegisterMeAsViewFactoryMethodName(factory) + "(action, this);");
        sourceWriter.outdent();
        sourceWriter.println("}");
        sourceWriter.outdent();
    }

    /**
     * Build the necessary OPBindingAction, depending of the presence or not of mandatory parameters.
     * 
     * @param factory
     *            all necessary methods for the view. Shouldn't be null.
     */
    protected void buildActionBinding(final OPBinderBuildingDataViewFactory factory) {
        assert factory != null : "parameter factory shouldn't be null";

        final String[] mandatoryParams = factory.getMandatoryParams();
        if (mandatoryParams.length == 0) {
            sourceWriter.println("final OPBindingAction action = OPBindingAction.actionFactory(\"" + factory.getActionName() + "\");");
        } else {
            sourceWriter.print("final String[] mandatoryParams = {");
            for (int i = 0; i < mandatoryParams.length; i++) {
                String mandatoryParam = mandatoryParams[i];
                if (i != 0) {
                    sourceWriter.print(", ");
                }
                sourceWriter.print("\"" + mandatoryParam + "\"");
            }
            sourceWriter.println("};");
            sourceWriter.println("final OPBindingAction action = OPBindingAction.actionFactory(\"" + factory.getActionName() + "\", mandatoryParams);");
        }
    }

    /**
     * Returns the name of the method registering the binder as view factory for the given view.
     * 
     * @param factory
     *            all necessary methods for the view. Shouldn't be null.
     * @return the name of the method registering the binder as view factory for the given view
     */
    protected String getRegisterMeAsViewFactoryMethodName(final OPBinderBuildingDataViewFactory factory) {
        assert factory != null : "parameter factory shouldn't be null";

        return METHOD_PREFIX_REGISTER_VIEW_FACTORY + factory.getServiceName() + factory.getResourceName() + factory.getActionName();
    }

    /**
     * Write the binder method registering itself as view factory for each views.
     */
    protected void writeViewsRegisterMeAsViewFactories() {
        assert buildingData != null : "builder data cannot be null";
        assert buildingData.getFactories() != null : "builder data factories cannot be null";

        // no view factory to register
        if (buildingData.getFactories().isEmpty()) {
            return;
        }

        for (OPBinderBuildingDataViewFactory factory : buildingData.getFactories()) {
            buildRegisterMeAsViewFactory(factory);
            sourceWriter.println();
        }
    }

    /**
     * Register the binder as the view factory for the corresponding view.
     * 
     * @param factory
     *            all the necessary data for the binded view. Shouldn't be null.
     */
    protected void buildRegisterMeAsViewFactory(final OPBinderBuildingDataViewFactory factory) {
        assert factory != null : "parameter factory shouldn't be null";

        sourceWriter.indent();
        sourceWriter.println("protected final native void " + getRegisterMeAsViewFactoryMethodName(factory) + "(final OPBindingAction action, final "
                + generatedBinderName + " binder) /*-{");
        sourceWriter.indent();
        sourceWriter.println("action.displayView = function(slotId, paramsjs) {");
        sourceWriter.indent();
        sourceWriter.println("return $entry(binder.@" + generatedBinderFullName + "::" + getViewFactoryCallbackMethodName(factory) + "(Ljava/lang/String;L"
                + TYPE_OPPARAMSJS + ";)(slotId, paramsjs));");
        sourceWriter.outdent();
        sourceWriter.println("}");
        sourceWriter.outdent();
        sourceWriter.println("}-*/;");
        sourceWriter.outdent();
    }

    /**
     * Returns the name of the method registered as view factory callback for the given view.
     * 
     * @param factory
     *            all necessary methods for the view. Shouldn't be null.
     * @return the name of the method registered as view factory callback for the given view
     */
    protected String getViewFactoryCallbackMethodName(final OPBinderBuildingDataViewFactory factory) {
        assert factory != null : "parameter factory shouldn't be null";

        return METHOD_PREFIX_VIEW_FACTORY_CALLBACK + factory.getServiceName() + factory.getResourceName() + factory.getActionName();
    }

    /**
     * Write the view factory callbacks for all views.
     */
    protected void writeViewsViewFactoryCallbacks() {
        assert buildingData != null : "builder data cannot be null";
        assert buildingData.getFactories() != null : "builder data factories cannot be null";

        // no view factory to register
        if (buildingData.getFactories().isEmpty()) {
            return;
        }

        for (OPBinderBuildingDataViewFactory factory : buildingData.getFactories()) {
            buildViewFactory(factory);
            sourceWriter.println();
        }
    }

    /**
     * Write the view factory callback for the corresponding view. Add the view to the slot using OPPartContainer.get(slotId).add(view).
     * 
     * @param factory
     *            all the necessary data for the binded view. Shouldn't be null.
     */
    protected void buildViewFactory(final OPBinderBuildingDataViewFactory factory) {
        assert factory != null;

        sourceWriter.indent();
        sourceWriter.println("public void " + getViewFactoryCallbackMethodName(factory) + "(final String slotId, final OPParamsJS paramsJS) {");
        sourceWriter.indent();
        sourceWriter.println("assert slotId != null : \"parameter slotId shouldn't be null\";");
        sourceWriter.println();
        sourceWriter.println("Panel slot;");
        sourceWriter.println("if (OPShell.SLOT_ID_ROOT.equals(slotId)) {");
        sourceWriter.indent();
        sourceWriter.println("slot = RootLayoutPanel.get();");
        sourceWriter.outdent();
        sourceWriter.println("} else {");
        sourceWriter.indent();
        sourceWriter.println("slot = OPPartContainer.get(slotId);");
        sourceWriter.outdent();
        sourceWriter.println("}");
        sourceWriter.println("assert slot != null : \"cannot find an element with the id \" + slotId + \" on the HTML page to be used as slot\";");
        sourceWriter.println();
        sourceWriter.println("final OPSlotProxy proxy = new OPSlotProxy(slotId);");
        sourceWriter.println("final OPMessagesDispatcher dispatcher = OPMessagesDispatcher.getDispatcherAttachedToId(slotId);");
        sourceWriter.println("if (dispatcher != null) {");
        sourceWriter.indent();
        sourceWriter.println("dispatcher.registerSlotProxy(proxy);");
        sourceWriter.outdent();
        sourceWriter.println("}");
        sourceWriter.println();
        sourceWriter.println("OPParams params = null;");
        sourceWriter.println("if (paramsJS != null) {");
        sourceWriter.indent();
        sourceWriter.println("params = paramsJS.toOPParams();");
        sourceWriter.outdent();
        sourceWriter.println("}");
        sourceWriter.println();
        sourceWriter.println("final Widget view = part." + factory.getFactory().getName() + "(params, proxy);");
        sourceWriter.println("assert view != null : \"view factory shouldn't return a null view\";");
        sourceWriter.println();
        sourceWriter.println("proxy.attachView(view);");
        sourceWriter.println();
        sourceWriter.println("slot.add(view);");
        sourceWriter.outdent();
        sourceWriter.println("proxy.onEmbeddedViewAttach();");
        sourceWriter.println();
        sourceWriter.println("if (dispatcher != null) {");
        sourceWriter.indent();
        sourceWriter.println("dispatcher.onEmbeddedViewAttach();");
        sourceWriter.outdent();
        sourceWriter.println("}");
        sourceWriter.println();
        sourceWriter.println("}");
        sourceWriter.outdent();
    }
    
    /**
     * Write the code returning the name of the service for the part binded.
     */
    protected void writeServiceHelper() {
        sourceWriter.indent();
        sourceWriter.println("public String getServiceName() {");
        sourceWriter.indent();
        sourceWriter.println("assert " + VAR_SERVICE_NAME + " != null : \"service name cannot be null\";");
        sourceWriter.println();
        sourceWriter.println("return " + VAR_SERVICE_NAME + ";");
        sourceWriter.outdent();
        sourceWriter.println("}");
        sourceWriter.outdent();
    }

    /**
     * Write the code returning the names of the resources for the part binded.
     */
    protected void writeResourcesHelper() {
        sourceWriter.indent();
        sourceWriter.println("public String[] getResourcesNames() {");
        sourceWriter.indent();
        sourceWriter.println("assert " + VAR_RESOURCES_NAMES + " != null : \"resources names cannot be null\";");
        sourceWriter.println();
        sourceWriter.println("return " + VAR_RESOURCES_NAMES + ";");
        sourceWriter.outdent();
        sourceWriter.println("}");
        sourceWriter.outdent();
    }

}
